//
// Created by F1997 on 2025/4/9.
//

#include "zce_decode.h"

// 解析数据包-zce
void zce_decode::parse_packet_zce2(const struct pcap_pkthdr* pkthdr, const u_char* packet, MarketData& marketData, std::ofstream &outFile) {
    std::cout << "解析数据包-payload的前若干字节" << std::endl;
/*        // 输出原始数据包内容，以十六进制形式输出
        for (int i = 0; i < 20 *//*pkthdr->caplen*//*; i++) {
            printf("%02x ", packet[i]);
            if ((i + 1) % 20 == 0) {
                printf("\n");
            }
        }
        printf("\n");*/

    // 解析以太网头部
    struct my_ether_header *eth_hdr = (struct my_ether_header *)packet;
    if (ntohs(eth_hdr->ether_type) == /*0*/0x0800) {
        // 解析IP头部
        struct my_ip_header *ip_hdr = (struct my_ip_header *)(packet + sizeof(struct my_ether_header));
        //printf("ip_v: %d\n", ip_hdr->ip_v);
        //printf("ip_src: %s\n", inet_ntoa(ip_hdr->ip_src));
        //printf("ip_dst: %s\n", inet_ntoa(ip_hdr->ip_dst));
        if (ip_hdr->ip_p == IPPROTO_TCP) {
            // 解析TCP头部
            struct my_tcp_header *tcp_hdr = (struct my_tcp_header *)(packet + sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4));

            // 计算payload的起始位置
            int payload_offset = sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4) + (tcp_hdr->th_off * 4);
            int payload_size = pkthdr->len - payload_offset;

/*                // 打印基本信息
                printf("Source IP: %s\n", inet_ntoa(ip_hdr->ip_src));
                printf("Destination IP: %s\n", inet_ntoa(ip_hdr->ip_dst));
                printf("Source Port: %d\n", ntohs(tcp_hdr->th_sport));
                printf("Destination Port: %d\n", ntohs(tcp_hdr->th_dport));
                printf("Payload Size: %d bytes\n", payload_size);

                // 打印payload
                if (payload_size > 0) {
                    printf("Payload:\n");
                    for (int i = 0; i < payload_size; ++i) {
                        printf("%02x ", packet[payload_offset + i]);
                        if ((i + 1) % 16 == 0) {
                            printf("\n");
                        }
                    }
                    printf("\n");
                }*/

        }
        else if (ip_hdr->ip_p == IPPROTO_UDP) {
            struct my_udp_header *udp_hdr = (struct my_udp_header *)(packet + sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4));
            std::cout << "udp_hdr->DstPort: " << ntohs(udp_hdr->DstPort) << std::endl;
            std::cout << "udp_hdr->len: " << ntohs(udp_hdr->len) << std::endl;
            // 计算payload的起始位置
            int payload_offset = sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4) + sizeof(struct my_udp_header); // udp包头长度固定为8?
            int payload_size = pkthdr->len - payload_offset;
            I32 offset = 0; // 累计所有消息体的长度

            while (offset < pkthdr->len) {
                // zce报文头
                struct ZCEHeader *zceHeader = (struct ZCEHeader *)(packet + payload_offset + offset);

                //printf("DstPort: %d\n", udp_hdr->DstPort);
                //printf("SrcPort: %d\n", udp_hdr->SrcPort);
                //printf("len: %d\n", udp_hdr->len);
                //printf("checkSum: %d\n", udp_hdr->checkSum);

                //printf("payload_offset: %d\n", payload_offset);
                //printf("payload_size: %d\n", payload_size);

                printf("mirpHeader->type_id: %d\n", zceHeader->msg_type);
                printf("msg_count: %d\n", zceHeader->msg_count);
                printf("%d\n", zceHeader->msg_len); // 小端序
                printf("msg_len: %d\n", ntohs(zceHeader->msg_len)); // 大端序
                //printf("mirpHeader->snap_time: %d\n", mirpHeader->snap_time);
                //printf("mirpHeader->snap_millisec: %d\n", mirpHeader->snap_millisec);
                //printf("mirpHeader->packet_no: %d\n", mirpHeader->packet_no); // 唯一编号，有可能重复，表示多个包是同一个行情
                printf("=================================\n");
                //struct MSGHeader* mhead2 = (struct MSGHeader*)(packet + payload_offset + zceHeader->msg_len);
                I32 datapos = 0;
                U16 pricesize = 0;
                U16 idx = 0;

                // 用 pkgType+msg_count+msg_len 来表示一个唯一的包？？？
/*                    switch (zceHeader->msg_type) { // 只处理发送频率高的报文
                        case PKG_TYPE_SGLCONT: //0x10 : // 单腿行情报文
                        { // switch case 中是不能定义对象的
                            marketData.packet_no = marketData.packet_no + 1;

                            MSGHeader *mhead = (struct MSGHeader*)(packet + payload_offset + 4); // 行情消息头
                            packet = packet + payload_offset + 4;
                            // 读取消息体长度
                            std::cout << "消息-length" << ntohs(mhead->length) << std::endl;

                            // 价格精度（价格字段需要除以价格精度获取实际值）
                            pricesize = my_read_uint16(&mhead->body[datapos]);
                            datapos += 2;
                            // 合约索引
                            idx = my_read_uint16(&mhead->body[datapos]);
                            datapos += 2;

                            // 只取时间戳
                            U32 temp = 0;
                            memcpy(&temp, packet, 4);
                            temp = ntohl(temp);

                            I32 sign = temp >> 31;             // 符号位(B31)
                            I32 fldidx   = (temp >> 26) & 0x1F;    // item索引(B30-B26)
                            I32 value    = temp & 0x3FFFFFF; // 数值部分(B25-B0) 0x03FFFFFF 取后26位
                            value   *= (0 == sign ? 1 : -1);

                            switch(fldidx){
                                case 7:
                                    std::cout << "updatetime=" << value << std::endl;
                                    break;
                                case 8:
                                    std::cout << "updatetimeusec=" << value << std::endl;
                                default:
                                    break;
                            }
                            std::cout << "pricesize=" << pricesize << ",idx=" << idx << ",fldidx="<< fldidx << std::endl;
                            //outFile << "pricesize=" << pricesize << ",idx=" << idx << std::endl;
                            break;
                        }
                        case 0x11 : // 组合行情报文
                            marketData.packet_no = marketData.packet_no +1;
                            break;
                        case 0x20 : // 单腿深度行情报文
                            marketData.packet_no = marketData.packet_no +1;
                            break;
                        case 0x21 : // 组合深度行情报文
                            marketData.packet_no = marketData.packet_no +1;
                            break;
                        default:
                            break;
                    }*/

                // 包内的数据提取到 MarketData
                //marketData.packet_no = mirpHeader->packet_no;

                // 下一个报文的偏移量
                std::cout << "offset=" << sizeof(ZCEHeader) << std::endl;
                offset += 4 + ntohs(zceHeader->msg_len);
            }

        }
    }
}

// 另一个，金老师的方式
void zce_decode::parse_packet_zce(const struct pcap_pkthdr* pkthdr, const u_char* packet, MarketData& marketData, std::ofstream &outFile) {
    //std::cout << "解析数据包-zce" << std::endl;

    // 解析以太网头部
    struct my_ether_header *eth_hdr = (struct my_ether_header *)packet;
    if (ntohs(eth_hdr->ether_type) == /*0*/0x0800) {
        // 解析IP头部
        struct my_ip_header *ip_hdr = (struct my_ip_header *)(packet + sizeof(struct my_ether_header));
        //printf("ip_v: %d\n", ip_hdr->ip_v);
        //printf("ip_src: %s\n", inet_ntoa(ip_hdr->ip_src));
        //printf("ip_dst: %s\n", inet_ntoa(ip_hdr->ip_dst));
        if (ip_hdr->ip_p == IPPROTO_TCP) {
            // 解析TCP头部
            struct my_tcp_header *tcp_hdr = (struct my_tcp_header *)(packet + sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4));

            // 计算payload的起始位置
            int payload_offset = sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4) + (tcp_hdr->th_off * 4);
            int payload_size = pkthdr->len - payload_offset;

        }
        else if (ip_hdr->ip_p == IPPROTO_UDP) {
            struct my_udp_header *udp_hdr = (struct my_udp_header *)(packet + sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4));
            //std::cout << "udp_hdr->DstPort: " << ntohs(udp_hdr->DstPort) << std::endl;
            //std::cout << "udp_hdr->len: " << ntohs(udp_hdr->len) << std::endl;

            // 计算payload的起始位置
            int payload_offset = sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4) + sizeof(struct my_udp_header); // udp包头长度固定为8? = 长度=42
            int payload_size = pkthdr->len - payload_offset; // 有效载荷的总长度
            I32 offset = 0; // 累计所有消息体的长度 = 已解析过的长度
            //std::cout << "offset:" << offset << " ,payload_size: " << payload_size << std::endl;

            // 单个udp包中，合约索引和时间戳只在第一个行情报文的第一个消息中才有，合约索引在第一个字段，时间戳在第16，18等字段
            int udp_idx = 0;
            int udp_time = 0;
            int udp_time_usec = 0;

            // 以16进制格式输出packet到文件
            outFile << "packet: " << marketData.pkgCount << std::endl;
            for (int i = 0; i < payload_size; ++i) {
                // std::hex << std::setw(2) << std::setfill('0') = 2位16进制字符串，不足的前面补0
                outFile << std::hex << std::setw(2) << std::setfill('0') << (int)packet[payload_offset + i] << " ";
                if ((i + 1) % 70 == 0) {
                    outFile<< std::endl;
                }
            }
            outFile << std::endl;
            outFile << std::dec;

            int mi = 0; // 一个行情报文中有多少个消息
            std::map<U32, std::string> contidx_map;

            while (offset < payload_size) { // udp中可包含行情报文/多个msg
                // zce报文头 = package head，包含消息类型、消息数量、正文长度
                struct mdp_pkg_head *head = (struct mdp_pkg_head *)(packet + payload_offset + offset);
                mdp_pkg_head* head2 = (mdp_pkg_head*)(packet + payload_offset + offset);
                //std::cout << "pkg_head->type: " << int(head->type) << " pkg_head->count: " << int(head->count) << " pkg_head->len: " << htons(head->len) << std::endl; // 大端序整数

                I32 datapos = 0;
                U16 pricesize = 0;
                U16 idx = 0;

                I32 pkglen = htons(head->len);
                I32 len = 0;

                for (I32 i = 0; i < head->count && len < pkglen; ++i) { // 循环解析报文中的每个消息体，
                    mi++;
                    //outFile << "行情报文中的消息数mi: " << mi << std::endl;
                    mdp_msg_head* mhead = (mdp_msg_head*)&head->data[len]; // 行情消息头-只有消息长度一个字段
                    //U16 msglen = read_uint16((C8*)&mhead->msglen);
                    U16 msglen = htons(mhead->msglen);
                    //std::cout << "mhead->msglen: " << htons(mhead->msglen) << " ,msglen" << msglen << std::endl; // 消息长度，包含长度标识的2字节
                    msglen -= 2;

                    switch (head->type) {
                        case PKG_TYPE_CONTRACT_IDX:         // 合约索引 每50毫秒推送一个UDP数据包
                            //on_contract_idx(mhead, msglen, 0 == i); // wg
                            break;
                        case PKG_TYPE_CONTRACT_INIT:        // 初始行情 每50毫秒推送一个UDP数据包，包含第一个字段4B(价格精度，合约索引)，第二个字段4B(符号位，字段类型，字段值)
                            //on_contract_init(mhead, msglen);
                            break;
                        case PKG_TYPE_SGLCONT:              // 单腿行情  250毫秒
                            //单腿合约的实时行情和补发行情全文使用的报文类型相同，但二者在数据组织方式和内容上有如下差别
                            //1）实时行情：
                            //    变化最新行情，未发送即表示该值无变化
                            //    一档通道中一个UDP数据包只有一个报文，报文内可以有多个消息，每个消息对应一个合约
                            //    深度通道中一个UDP数据包可以包含多个报文，一个报文只有一个消息（需配合0x20报文获取合约最新行情）
                            //2）补发行情全文：
                            //    在每次实时行情发送之后立刻补发一个单腿合约行情全文，补发合约的顺序与合约索引一致，约8分钟可以遍历一轮所有单腿合约
                            //    包含所有字段，且保证所有字段为最新值
                            //    补发行情全文的作用是便于用户补齐数据
                            // 最多4*32字节
                        {
                            I32 datapos = 0; // 字段，已解析的长度

                            I64 matchval_part1 = 0;  // I64保证数据左移26位不会溢出
                            I64 matchval_part2 = 0;

                            // 价格精度（价格字段需要除以价格精度获取实际值）
                            U16 pricesize = htons(mhead->data[datapos]);
                            datapos += 2;

                            // 合约索引
                            U16 idx = htons(mhead->data[datapos]);
                            udp_idx = idx;
                            datapos += 2;

                            //std::cout << "===>pricesize=" << pricesize << ",idx=" << idx << ",datapos=" << datapos << ",msglen=" << msglen << std::endl;
                            outFile << "===>PKG_TYPE_SGLCONT," << mhead->msglen << std::endl;

                            uint32_t msg_cnt = 0;
                            while (datapos < msglen) { // 从第二个字段开始，逐位解析
                                ++msg_cnt;
                                I32 fldidx = 0;
                                I32 value = 0;
                                //datapos += get_int_value(&mhead->data[datapos], fldidx, value);

                                my_f_h my_f = *(my_f_h*)&mhead->data[datapos];
                                U32 temp = 0;
                                memcpy(&temp, &mhead->data[datapos], 4);
                                temp = ntohl(temp); // 从大端序转成小端序

                                I32 sign = temp >> 31;             // 符号位(B31)，0表示正数，1表示负数，通过右移31位获取最高位（符号位）
                                fldidx   = (temp >> 26) & 0x1F;    // item索引(B30-B26)，字段类型，1-6，通过右移26位后与0x1F（5位掩码）按位与操作，获取字段索引（位于B30-B26）
                                outFile << "temp=" << temp;
                                value    = temp & 0x3FFFFFF; // 数值部分(B25-B0) 0x03FFFFFF 取后26位，字段对应的具体值，整数，通过与（0x03FFFFFF，26位掩码）按位与操作，获取数值部分（位于B25-B0）
                                outFile << ":" << value << std::endl;
                                //std::cout << "字段：sign="<< sign <<" ,fldidx=" << fldidx << " ,value=" << value << std::endl;
                                //outFile << "sign="<< sign <<" ,fldidx=" << fldidx << " ,value=" << value << std::endl;

                                value   *= (0 == sign ? 1 : -1); // 按照符号位是否为0来加正负号


                                datapos += 4;
                                switch (fldidx) { // 26个字段
                                    case 1:  // 开盘价
                                        //printf("开盘价：         %f\n", (double)value / pricesize);
                                        //p_sglcont->OpenPrice = (double)value / pricesize;
                                        break;
                                    case 2:  // 最高价
                                        //printf("最高价：         %f\n", (double)value / pricesize);
                                        //p_sglcont->HighPrice = (double)value / pricesize;
                                        break;
                                    case 3:  // 最低价
                                        //printf("最低价：         %f\n", (double)value / pricesize);
                                        //p_sglcont->LowPrice = (double)value / pricesize;
                                        break;
                                    case 4:  // 最新价
                                        //printf("最新价：         %f\n", (double)value / pricesize);
                                        //p_sglcont->LastPrice = (double)value / pricesize;
                                        break;
                                    case 5:  // 买价
                                        //printf("买价：           %f\n", (double)value / pricesize);
                                        //p_sglcont->BidPrice = (double)value / pricesize;
                                        break;
                                    case 6:  // 卖价
                                        //printf("卖价：           %f\n", (double)value / pricesize);
                                        //p_sglcont->AskPrice = (double)value / pricesize;
                                        break;
                                    case 7:  // 买量
                                        //printf("买量：           %d\n", value);
                                        //p_sglcont->BidLot = value;
                                        break;
                                    case 8:  // 卖量
                                        //printf("卖量：           %d\n", value);
                                        //p_sglcont->AskLot = value;
                                        break;
                                    case 9:  // 成交量
                                        //printf("成交量：         %d\n", value);
                                        //p_sglcont->Volume = value;
                                        break;
                                    case 10:  // 持仓量
                                        //printf("持仓量：         %d\n", value);
                                        //p_sglcont->OpenInterest = value;
                                        break;
                                    case 11:  // 组合买价
                                        //printf("组合买入价：     %f\n", (double)value / pricesize);
                                        //p_sglcont->DeriveBidPrice = (double)value / pricesize;
                                        break;
                                    case 12:  // 组合卖价
                                        //printf("组合卖出价：     %f\n", (double)value / pricesize);
                                        //p_sglcont->DeriveAskPrice = (double)value / pricesize;
                                        break;
                                    case 13:  // 组合买量
                                        //printf("组合买量：       %d\n", value);
                                        //p_sglcont->DeriveBidLot = value;
                                        break;
                                    case 14:  // 组合卖量
                                        //printf("组合卖量：       %d\n", value);
                                        //p_sglcont->DeriveAskLot = value;
                                        break;
                                    case 15:  // 均价
                                        //printf("均价：           %f\n", (double)value / pricesize);
                                        //p_sglcont->AvgPrice = (double)value / pricesize;
                                        break;
                                    case 16:  // 秒级时间戳
                                        // 在单个UDP数据包中，UpdateTime和UpdateTimeUsec字段只在第一个行情报文的第一个消息中存放，数据包内后续合约的更新时间使用这两个字段即可
                                        //printf("秒级时间戳：         %d\n", value);
                                        //p_sglcont->m_updatetime = value; // updatetime
                                        udp_time = value;
                                        break;
                                    case 17:  // 结算价
                                        //printf("结算价：         %f\n", (double)value / pricesize);
                                        //p_sglcont->ClearPrice = (double)value / pricesize;
                                        break;
                                    case 18:  // 微秒级时间戳
                                        // 微秒时间戳UpdateTimeUsec是行情系统根据特定信号自行模拟的，非真实时间戳，目的是方便用户将同一秒内接收到的不同批次的行情区分开
                                        // 在单个UDP数据包中，UpdateTime和UpdateTimeUsec字段只在第一个行情报文的第一个消息中存放，数据包内后续合约的更新时间使用这两个字段即可
                                        //printf("微秒级时间戳：         %d\n", value);
                                        udp_time_usec = value;
                                        //p_sglcont->m_updatetimeusec = value;
                                        break;
                                    case 19:  // 总成交金额(part1)  总成交金额用两个字段表示，读取时先以整数形式将part1和part2读出，然后使用 (part1 << 26) | part2将两个整数拼接起来，最后根据价格精度将其转换为精确的总成交金额
                                        matchval_part1 = value;
                                        break;
                                    case 20:  // 总成交金额(part2)
                                        matchval_part2 = value;
                                        break;
                                    case 21:  // 历史最高价
                                        //printf("历史最高价：     %f\n", (double)value / pricesize);
                                        //p_sglcont->LifeHighPrice = (double)value / pricesize;
                                        break;
                                    case 22:  // 历史最低价
                                        //printf("历史最低价：     %f\n", (double)value / pricesize);
                                        //p_sglcont->LifeLowPrice = (double)value / pricesize;
                                        break;
                                    case 23:  // 总买量
                                        //printf("总买量：         %d\n", value);
                                        //p_sglcont->VolBidLot = value;
                                        break;
                                    case 24:  // 买均价
                                        //printf("买均价：         %f\n", (double)value / pricesize);
                                        //p_sglcont->BidAvgPrice = (double)value / pricesize;
                                        break;
                                    case 25:  // 总卖量
                                        //printf("总卖量：         %d\n", value);
                                        //p_sglcont->VolAskLot = value;
                                        break;
                                    case 26:  // 卖均价
                                        //printf("卖均价：         %f\n", (double)value / pricesize);
                                        //p_sglcont->AskAvgPrice = (double)value / pricesize;
                                        break;
                                    default:
                                        printf("on_sglcont error item: %d, value: %d\n", fldidx, value);
                                        break;
                                }
                                // 过滤补发行情
                                if (188 > mhead->msglen) {
                                    //std::cout <<  "===>过滤补发行情-单腿" << std::endl;
                                    udp_time = 0;
                                }

                            }

                        }
                            break;
                        case PKG_TYPE_CMBCONT:              // 组合行情 250毫秒
                            //组合合约的实时行情和补发行情全文使用的报文类型相同，但二者在数据组织方式和内容上有如下差别
                            //1）实时行情：
                            //    每250毫秒发送一批数据，包含从上次发送到现在有变化的组合合约的最新行情
                            //    报文中只包含变化的字段，未发送即表示该值无变化
                            //    一档通道中一个UDP数据包只有一个报文，报文内可以有多个消息，每个消息对应一个合约
                            //    深度通道中一个UDP数据包可以包含多个报文，一个报文只有一个消息（需配合0x21报文获取合约最新行情）
                            //2）补发行情全文：
                            //    在每次实时行情发送之后立刻补发一个单腿合约行情全文和一个组合合约行情全文，补发合约的顺序与合约索引一致，约3分钟可以遍历一轮所有组合合约
                            //    包含所有字段，且保证所有字段为最新值
                            //    一档通道和深度通道中，一个UDP数据包只有一个报文，报文只有一个消息（深度通道需要配合0x21报文获取所有字段），每个消息对应一个合约
                            //    补发行情全文的作用是便于用户补齐数据
                        {
                            I32 datapos = 0; // 分类行情中已解析的长度

                            // 价格精度（价格字段需要除以价格精度获取实际值）
                            U16 pricesize = htons(mhead->data[datapos]);
                            datapos += 2;

                            // 合约索引
                            U16 idx = htons(mhead->data[datapos]);
                            datapos += 2;

                            //std::cout << "===>pricesize=" << pricesize << ",idx=" << idx << "," << sizeof(mdp_pkg_head) << std::endl; // wg

                            uint32_t msg_cnt = 0;
                            while (datapos < len) {
                                ++msg_cnt;
                                I32 fldidx = 0;
                                I32 value = 0;

                                my_f_h my_f = *(my_f_h*)&mhead->data[datapos];
                                U32 temp = 0;
                                memcpy(&temp, &mhead->data[datapos], 4);
                                temp = ntohl(temp); // 从大端序转成小端序

                                I32 sign = temp >> 31;             // 符号位(B31)，0表示正数，1表示负数，通过右移31位获取最高位（符号位）
                                fldidx   = (temp >> 26) & 0x1F;    // item索引(B30-B26)，字段类型，1-6，通过右移26位后与0x1F（5位掩码）按位与操作，获取字段索引（位于B30-B26）
                                value    = temp & 0x3FFFFFF; // 数值部分(B25-B0) 0x03FFFFFF 取后26位，字段对应的具体值，整数，通过与（0x03FFFFFF，26位掩码）按位与操作，获取数值部分（位于B25-B0）
                                //std::cout << "字段：sign="<< sign <<" ,fldidx=" << fldidx << " ,value=" << value << std::endl;
                                //outFile << "sign="<< sign <<" ,fldidx=" << fldidx << " ,value=" << value << std::endl;
                                value   *= (0 == sign ? 1 : -1); // 按照符号位是否为0来加正负号

                                datapos += 4;

                                switch (fldidx) { // 8个字段
                                    case 1:  // 买价
                                        //printf("买价：           %f\n", (double)value / pricesize);
                                        //p_cmbcont->BidPrice = (double)value / pricesize;
                                        break;
                                    case 2:  // 卖价
                                        //printf("卖价             %f\n", (double)value / pricesize);
                                        //p_cmbcont->AskPrice = (double)value / pricesize;
                                        break;
                                    case 3:  // 买量
                                        //printf("买量：           %d\n", value);
                                        //p_cmbcont->BidLot = value;
                                        break;
                                    case 4:  // 卖量
                                        //printf("卖量：           %d\n", value);
                                        //p_cmbcont->AskLot = value;
                                        break;
                                    case 5:  // 总买量
                                        //printf("总买量：         %d\n", value);
                                        //p_cmbcont->VolBidLot = value;
                                        break;
                                    case 6:  // 总卖量
                                        //printf("总卖量：         %d\n", value);
                                        //p_cmbcont->VolAskLot = value;
                                        break;
                                        //在单个UDP数据包中，UpdateTime和UpdateTimeUsec字段只在第一个行情报文的第一个消息中存放，数据包内后续合约的更新时间使用这两个字段即可
                                    case 7: // 秒级时间戳
                                        //p_cmbcont->m_updatetime = value;
                                        udp_time = value;
                                        break;
                                    case 8: // 微秒级时间戳
                                        //微秒时间戳UpdateTimeUsec是行情系统根据特定信号自行模拟的，非真实时间戳，目的是方便用户将同一秒内接收到的不同批次的行情区分开
                                        //p_cmbcont->m_updatetimeusec = value;
                                        udp_time_usec = value;
                                        break;
                                    default:
                                        printf("on_cmbcont error item: %d, value: %d\n", fldidx, value);
                                        break;
                                }

                                // 过滤补发行情
                                if (188 < mhead->msglen) {
                                    std::cout <<  "===>过滤补发行情" << std::endl;
                                    udp_time = 0;
                                }
                            }
                            //
                        }

                            break;
                        case PKG_TYPE_PRODUCT_STATUS:       // 品种交易状态  广播报文包含通知、公告等消息，也会推送品种状态变化消息
                            //on_product_status(mhead);
                            break;
                        case PKG_TYPE_QUOTE_REQ:            // 报价请求
                            //on_quote_req(mhead);
                            break;
                        case PKG_TYPE_MARKET_STATUS:        // 市场交易状态
                            //on_market_status(mhead);
                            break;
                        case PKG_TYPE_SGLCONT_DEPTH:        // 单腿深度行情  仅在深度通道中包含，没有时间戳
                            //同一合约的两个报文同时发送，0x10在前，0x20在后，且保证在同一个UDP数据包内
                            //注意的是，在发送变化行情时只发送合约有变化的字段，因此存在一个合约的行情只有0x10报文或只有0x20报文的情况
                            //单腿深度行情报文由多个字段组成，最多可以包含11个字段
/*                                if (!m_idxdone || !m_initdone)
                                    break;

                                pre_sglcont_depth();
                                deep_sql_idx = on_sglcont_depth(mhead, msglen);
                                update_sglcont();*/

                        {
                            I32 datapos = 0;

                            // 价格精度（价格字段需要除以价格精度获取实际值）
                            //U16 pricesize = read_uint16(&mhead->data[datapos]);
                            U16 pricesize = htons(mhead->data[datapos]);
                            datapos += 2;

                            // 合约索引
                            //p_contract_depth->idx = read_uint16(&mhead->data[datapos]);
                            U16 idx = htons(mhead->data[datapos]);
                            datapos += 2;

                            //单腿深度行情报文中不含时间戳字段，时间戳可从对应单腿行情报文中获取
                            while (datapos < len) {
                                I32 fldidx = 0;
                                I32 price = 0;
                                I32 qty = 0;
                                I32 ordcnt = 0;

                                datapos += 8;
                                F64 dprice = (F64)price / pricesize;

                                switch (fldidx) {
                                    case 1:
                                        //p_contract_depth->Bid1_price = dprice;     // 买1价
                                        //p_contract_depth->Bid1_qty = qty;          // 买1量
                                        //p_contract_depth->Bid1_ordcnt = ordcnt;    // 买1订单数
                                        break;
                                    case 2:
                                        //p_contract_depth->Ask1_price = dprice;     // 卖1价
                                        //p_contract_depth->Ask1_qty = qty;          // 卖1量
                                        //p_contract_depth->Ask1_ordcnt = ordcnt;    // 卖1订单数
                                        break;
                                    case 3:
                                        //p_contract_depth->Bid2_price = dprice;     // 买2价
                                        //p_contract_depth->Bid2_qty = qty;          // 买2量
                                        //p_contract_depth->Bid2_ordcnt = ordcnt;    // 买2订单数
                                        break;
                                    case 4:
                                        //p_contract_depth->Ask2_price = dprice;     // 卖2价
                                        //p_contract_depth->Ask2_qty = qty;          // 卖2量
                                        //p_contract_depth->Ask2_ordcnt = ordcnt;    // 卖2订单数
                                        break;
                                    case 5:
                                        //p_contract_depth->Bid3_price = dprice;     // 买3价
                                        //p_contract_depth->Bid3_qty = qty;          // 买3量
                                        //p_contract_depth->Bid3_ordcnt = ordcnt;    // 买3订单数
                                        break;
                                    case 6:
                                        //p_contract_depth->Ask3_price = dprice;     // 卖3价
                                        //p_contract_depth->Ask3_qty = qty;          // 卖3量
                                        //p_contract_depth->Ask3_ordcnt = ordcnt;    // 卖3订单数
                                        break;
                                    case 7:
                                        //p_contract_depth->Bid4_price = dprice;     // 买4价
                                        //p_contract_depth->Bid4_qty = qty;          // 买4量
                                        //p_contract_depth->Bid4_ordcnt = ordcnt;    // 买4订单数
                                        break;
                                    case 8:
                                        //p_contract_depth->Ask4_price = dprice;     // 卖4价
                                        //p_contract_depth->Ask4_qty = qty;          // 卖4量
                                        //p_contract_depth->Ask4_ordcnt = ordcnt;    // 卖4订单数
                                        break;
                                    case 9:
                                        //p_contract_depth->Bid5_price = dprice;     // 买5价
                                        //p_contract_depth->Bid5_qty = qty;          // 买5量
                                        //p_contract_depth->Bid5_ordcnt = ordcnt;    // 买5订单数
                                        break;
                                    case 10:
                                        //p_contract_depth->Ask5_price = dprice;     // 卖5价
                                        //p_contract_depth->Ask5_qty = qty;          // 卖5量
                                        //p_contract_depth->Ask5_ordcnt = ordcnt;    // 卖5订单数
                                        break;
                                    default:
                                        printf("on_sglcont_depth error item: %d, value: %d\n", fldidx, price);
                                        break;
                                }
                            }
                        }
                            break;
                        case PKG_TYPE_CMBCONT_DEPTH:        // 组合深度行情，没时间戳
/*                                if (!m_idxdone || !m_initdone)
                                    break;

                                pre_cmbcont_depth();
                                deep_cmb_idx = on_cmbcont_depth(mhead, msglen);
                                update_cmbcont();*/

                        {
                            I32 datapos = 0;

                            // 价格精度（价格字段需要除以价格精度获取实际值）
                            //U16 pricesize = read_uint16(&mhead->data[datapos]);
                            U16 pricesize = htons(mhead->data[datapos]);
                            datapos += 2;

                            // 合约索引
                            //p_contract_depth_combe->idx = read_uint16(&mhead->data[datapos]);
                            U16 idx = htons(mhead->data[datapos]);
                            datapos += 2;

                            while (datapos < len) {
                                I32 fldidx = 0;
                                I32 price = 0;
                                I32 qty = 0;
                                I32 ordcnt = 0;

                                datapos += 8;
                                F64 dprice = (F64)price / pricesize;

                                switch (fldidx) {
                                    case 1:
                                        //printf("买1：            %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Bid1_price = dprice;     // 买1价
                                        //p_contract_depth_combe->Bid1_qty = qty;          // 买1量
                                        //p_contract_depth_combe->Bid1_ordcnt = ordcnt;    // 买1订单数
                                        break;
                                    case 2:
                                        //printf("卖1：            %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Ask1_price = dprice;     // 卖1价
                                        //p_contract_depth_combe->Ask1_qty = qty;          // 卖1量
                                        //p_contract_depth_combe->Ask1_ordcnt = ordcnt;    // 卖1订单数
                                        break;
                                    case 3:
                                        //printf("买2:             %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Bid2_price = dprice;     // 买2价
                                        //p_contract_depth_combe->Bid2_qty = qty;          // 买2量
                                        //p_contract_depth_combe->Bid2_ordcnt = ordcnt;    // 买2订单数
                                        break;
                                    case 4:
                                        //printf("卖2:             %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Ask2_price = dprice;     // 卖2价
                                        //p_contract_depth_combe->Ask2_qty = qty;          // 卖2量
                                        //p_contract_depth_combe->Ask2_ordcnt = ordcnt;    // 卖2订单数
                                        break;
                                    case 5:
                                        //printf("买3:             %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Bid3_price = dprice;     // 买3价
                                        //p_contract_depth_combe->Bid3_qty = qty;          // 买3量
                                        //p_contract_depth_combe->Bid3_ordcnt = ordcnt;    // 买3订单数
                                        break;
                                    case 6:
                                        //printf("卖3:             %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Ask3_price = dprice;     // 卖3价
                                        //p_contract_depth_combe->Ask3_qty = qty;          // 卖3量
                                        //p_contract_depth_combe->Ask3_ordcnt = ordcnt;    // 卖3订单数
                                        break;
                                    case 7:
                                        //printf("买4:             %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Bid4_price = dprice;     // 买4价
                                        //p_contract_depth_combe->Bid4_qty = qty;          // 买4量
                                        //p_contract_depth_combe->Bid4_ordcnt = ordcnt;    // 买4订单数
                                        break;
                                    case 8:
                                        //printf("卖4:             %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Ask4_price = dprice;     // 卖4价
                                        //p_contract_depth_combe->Ask4_qty = qty;          // 卖4量
                                        //p_contract_depth_combe->Ask4_ordcnt = ordcnt;    // 卖4订单数
                                        break;
                                    case 9:
                                        //printf("买5:             %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Bid5_price = dprice;     // 买5价
                                        //p_contract_depth_combe->Bid5_qty = qty;          // 买5量
                                        //p_contract_depth_combe->Bid5_ordcnt = ordcnt;    // 买5订单数
                                        break;
                                    case 10:
                                        //printf("卖5:             %f, %d, %d\n",  dprice, qty, ordcnt);
                                        //p_contract_depth_combe->Ask5_price = dprice;     // 卖5价
                                        //p_contract_depth_combe->Ask5_qty = qty;          // 卖5量
                                        //p_contract_depth_combe->Ask5_ordcnt = ordcnt;    // 卖5订单数
                                        break;
                                    default:
                                        printf("on_cmbcont_depth error item: %d, value: %d\n", fldidx, price);
                                        break;
                                }
                            }
                        }
                            break;
                        default:
                            printf("recv unknown package type: 0x%02x\n", head->type);
                            break;
                    }

                    len += sizeof(mdp_msg_head) + msglen; // 下一个消息头 = 当前消息头 + 当前消息长度
                }
                offset += sizeof(mdp_pkg_head) + pkglen;
                if (udp_time != 0) {
                    //outFile << marketData.pkgCount << "," << mi << "," << udp_idx << "," << udp_time << "," << udp_time_usec << "," << pkthdr->ts.tv_sec << "," << pkthdr->ts.tv_usec << "," << std::endl;
                }
                // 插入到 contidx_map
                //contidx_map[udp_idx] = udp_time + "," + udp_time_usec + "," + pkthdr->ts.tv_sec + "," + pkthdr->ts.tv_usec;

                // 包内的数据提取到 MarketData
                //marketData.packet_no = mirpHeader->packet_no;

                // 下一个报文的偏移量
                //std::cout << "next_offset=" << sizeof(mdp_pkg_head) << std::endl;
            } // while
            if (udp_time != 0) { // 一个udp输出一次
                outFile << udp_idx << "," << udp_time << "," << udp_time_usec << "," << pkthdr->ts.tv_sec << ","
                        << pkthdr->ts.tv_usec << std::endl;
            }


        }
    }
}


// 从指定的字符缓冲区中读取一个16位无符号整数
U16 zce_decode::my_read_uint16(C8* buf) {
    U8* tbuf = (U8*)buf;
    return (tbuf[0] << 8) + tbuf[1]; // 按大端序（Big-Endian）方式将两个字节组合成16位整数：高字节左移8位后与低字节相加
}
